﻿package org.atk.components 
{
	import flash.display.BlendMode;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	import org.aswing.geom.IntPoint;
	import org.atk.icons.IcnTreeGridView;
	import org.atk.objects.ActionRectangle;
	import org.atk.objects.SpriteButton;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import org.aswing.ASColor;
	import org.aswing.AssetIcon;
	import org.aswing.AttachIcon;
	import org.aswing.Component;
	import org.aswing.Cursor;
	import org.aswing.CursorManager;
	import org.aswing.geom.IntRectangle;
	import org.aswing.util.ArrayList;
	import org.atk.components.treegridview.TreeGridViewItem;
	import org.atk.components.treegridview.TreeGridViewColumn;
	import org.atk.components.treegridview.TreeGridViewModel;
	import org.atk.components.treegridview.TreeGridViewRenderer;
	import org.flashdevelop.utils.FlashConnect;
	
	/**
	 * ...
	 * @author AeTky
	 */
	public class ATreeGridView extends Component{
		private var _ROOT:TreeGridViewItem = new TreeGridViewItem(null);
		
		private var dataModel:TreeGridViewModel = new TreeGridViewModel();
		
		private var colCount:int = 1;
		
		private var minimumColWidth:int = 50;
		private var headerHeight:int = 20;
		private var Columns:Array = new Array();
		
		private var paddingLeft:int = 5;
		private var paddingFolderIcon:int = 10;
		private var cellSpacing:int = 2;
		private var cellPadding:int = 0;
		private var resizerPadding:int = 7;
		
		private var cellHeight:int = 21;
		private var isFixedCellHeight:Boolean = false;
		private var treeColumn:int = 0;
		
		private var lastHeight:int = 0;
		private var lastWidth:int = 0;
		
		private var overlay:Sprite;
		private var columnBg:Sprite;
		private var selectSprite:Sprite;
		private var movingSprite:Sprite;
		
		private var actions:Array = new Array();
/***/
		private var currentResizeColumn:TreeGridViewColumn= null;
		public static var moNope:int = 0;
		public static var moColumnResize:int = 1;
		
		private var mouseOperation:int=moNope;
/***/
		private var selected:ActionRectangle = null;
		private var selectedCell:ActionRectangle = null;
		private var selectedBgColor:ASColor = ASColor.getASColor(200, 200, 200, .25);
		private var selectedBorderColor:ASColor = ASColor.getASColor(0, 0, 0);
		private var selectedCellBgColor:ASColor = ASColor.getASColor(255,128,0,.128);
		
		private var tgvRenderer:TreeGridViewRenderer = new TreeGridViewRenderer();

		public function ATreeGridView(){
			super();
			IcnTreeGridView.create();
			
			columnBg = new Sprite();
			columnBg.mouseEnabled = false;
			this.addChild(columnBg);
			
			
			selectSprite = new Sprite();
			selectSprite.mouseEnabled = false;
			this.addChild(selectSprite);
			
			overlay = new Sprite();
			overlay.mouseEnabled = false;
			this.addChild(overlay);	
			
			movingSprite = new Sprite();
			movingSprite.mouseEnabled = false;
			this.addChild(movingSprite);
			
			this.setFocusable(true);
			this.setFocusableSet(true);
			this.addEventListener(KeyboardEvent.KEY_UP, onTvKeyUp);
			this.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, onTvMouseFocusChange);
			this.addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);
			
			this.addEventListener(MouseEvent.MOUSE_DOWN, onTVMouseDown);
			this.addEventListener(MouseEvent.MOUSE_UP, onTVMouseUp);
			this.addEventListener(MouseEvent.MOUSE_MOVE, onTVMouseMove);
			this.addEventListener(MouseEvent.CLICK, onTVMouseClick);
		}
		
		private function onFocusOut(e:FocusEvent):void {
			changeRowSelection(selected);
			changeCellSelection(selectedCell);
		}
		private function handleMouseClick():void {
			var actArray:Array = getActionFromMousePoint();
			if (actArray.length == 0) {
				var pp:IntPoint = new IntPoint(this.mouseX, this.mouseY);
				if (!this.getRect(this.parent).containsPoint(pp.toPoint())){
					//FlashConnect.trace("focusChanged");
					return;
				}
				
				changeRowSelection(null);
				changeCellSelection(null);
			}
			var needRedraw:Boolean = false;
			var idx:int;
			var selectedRow:ActionRectangle = null;
			var selectedCell:ActionRectangle = null;
			for (idx = 0; idx < actArray.length;idx++ ) {
				var act:ActionRectangle = actArray[idx];
				switch(act.ActionType) {
					case ActionRectangle.atNone:
					case ActionRectangle.atResizer: return;
					case ActionRectangle.atFolderIcon:
						var item:TreeGridViewItem = null;
						if (act.Attachment != null)
							item = act.Attachment as TreeGridViewItem;
						if (item == null) break;
						item.IsOpen = !item.IsOpen;
						needRedraw = true;
						break;
					case ActionRectangle.atRow:
						selectedRow = act;
						break;
					case ActionRectangle.atCell:
						selectedCell = act;
						break;
				}
			}
			if(selectedRow!=null){
				changeRowSelection(selectedRow);
			}else {
				changeRowSelection(null);
			}
			if(selectedCell!=null){
				changeCellSelection(selectedCell);
			}else {
				changeCellSelection(null);
			}
			
			if(needRedraw)
				this.repaint();
		}
		private function onTvKeyUp(e:KeyboardEvent):void {
			var dd:int = 0;
			var cc:int = 0;
			switch(e.keyCode) {
				case Keyboard.UP: dd--; break;
				case Keyboard.DOWN: dd++; break;
				case Keyboard.PAGE_UP: dd -= 10; break;
				case Keyboard.PAGE_DOWN: dd += 10; break;
				case Keyboard.LEFT: cc--; break;
				case Keyboard.RIGHT: cc++; break;
				
				case Keyboard.TAB:

				break;
				
				default: return;
			}
			var ar:ActionRectangle = getKeyRowAction(selected, dd);
			var arCell:ActionRectangle = getKeyCellAction(ar, cc);
			changeRowSelection(ar);
			changeCellSelection(arCell);
		}
		
		private function onTvMouseFocusChange(e:FocusEvent):void {
			handleMouseClick();
		}
		private function getActionFromMousePoint():Array{//ActionRectangle {
			var idx:int = 0;
			var pp:IntPoint = new IntPoint(this.mouseX, this.mouseY);
			var retArr:Array = new Array();
			for (idx = 0; idx < actions.length; idx++) {
				var ar:ActionRectangle = actions[idx];
				
				if (!ar.ActRect.containsPoint(pp)) continue;
				if (ar.Attachment == null) continue;
				
				retArr.push(ar);
				//return ar;
			}
			return retArr;
			//return null;
		}
		private function getKeyCellAction(row:ActionRectangle, deltaCol:int):ActionRectangle {
			if (row == null) return null;
			var searchIdx:int = 0;
			if (selectedCell != null) searchIdx=selectedCell.ColIndex;
			searchIdx += deltaCol;
			
			if(searchIdx>=Columns.length)
				searchIdx = Columns.length - 1;
			if(searchIdx<0)
				searchIdx = 0;
				
				
			var idx:int = 0;
			for (idx = 0; idx < actions.length; idx++) {
				var ar:ActionRectangle = actions[idx];
				if (ar.ActionType != ActionRectangle.atCell) continue;
				if (ar.Attachment != row.Attachment) continue;
				
				if (searchIdx != ar.ColIndex) continue;
				
				return ar;
			}
			return null;
		}
		private function getKeyRowAction(obj:Object,at:int):ActionRectangle {
			var idx:int = 0;
			var atIdx:int = 0;
			var mIdx:int = 0;
			var ret:ActionRectangle = null;
			var useArr:Array = new Array();
			for (idx = 0; idx < actions.length; idx++) {
				var ar:ActionRectangle = actions[idx];
				if (ar.ActionType != ActionRectangle.atRow) continue;
				
				atIdx = useArr.push(ar);
				if (ar == obj){
					ret = ar;
					mIdx = atIdx-1;
				}
				
			}
			if (obj == null && useArr.length > 0) {
				mIdx = 0;
				ret = useArr[0];
				at = 0;
			}
			if (ret == null) return null;
			if (useArr.length == 0) return null;
			///key...
			mIdx += at;
			//FlashConnect.trace(mIdx);
			if (mIdx < 0) mIdx = 0;
			if (mIdx >= useArr.length) mIdx = useArr.length - 1;
			try{
				return useArr[mIdx] as ActionRectangle;
			}catch (ex:*) {}
			return null;
		}
		private function onTVMouseClick(e:MouseEvent):void {
			handleMouseClick();
		}
		private function changeCellSelection(actRect:ActionRectangle):void{			
			try {
				if (actRect == null) return;
				var bounds:IntRectangle = actRect.ActRect;
				
				
				
				selectSprite.graphics.lineStyle(undefined);
			
				selectSprite.graphics.beginFill(selectedCellBgColor.getRGB());// , selectedBgColor.getAlpha());
				selectSprite.graphics.drawRect(bounds.x-1, bounds.y+1, bounds.width-1, bounds.height-1);
				selectSprite.graphics.endFill();
				
			}finally {
				if (actRect != selectedCell) {
					//emitSelectionChanged
				}
				selectedCell = actRect;
			}
			
		}
		private function changeRowSelection(actRect:ActionRectangle):void{
			selectSprite.graphics.clear();
			try {
				if (actRect == null) return;
				var bounds:IntRectangle = actRect.ActRect;
				
				var color:ASColor = selectedBgColor;
				if (!isFocusOwner()) {
					selectSprite.blendMode = BlendMode.SUBTRACT;
				}else {
					selectSprite.blendMode = BlendMode.NORMAL;
				}
				
				selectSprite.graphics.lineStyle(undefined);// , selectedBorderColor.getRGB(), selectedBorderColor.getAlpha());
				selectSprite.graphics.beginFill(color.getRGB(), color.getAlpha());
				selectSprite.graphics.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
				selectSprite.graphics.endFill();
				selectSprite.graphics.lineStyle(undefined);
			}finally {				
				if (actRect != selected) {
					//emitSelectionChanged
				}
				selected = actRect;
			}
			
		}
		
		private function onTVMouseUp(e:MouseEvent):void {
			if (mouseOperation == moNope) return;
			try {
				if (currentResizeColumn == null) return;
				if (currentResizeColumn.Size == oldColWidth) return;
				this.repaint();
			}finally {
				CursorManager.getManager().hideCustomCursor(mCursor);
				resetMovingSprite();
				mouseOperation = moNope;
				currentResizeColumn = null;
			}
		}
		private var oldMouseX:int;
		private var oldMouseY:int;
		private var oldColWidth:int;
		private var mCursor:DisplayObject;
		
		
		
		private function onTVMouseDown(e:MouseEvent):void {
			currentResizeColumn = null;
			if (!e.buttonDown) return;
			
			var idx:int = 0;
			var pp:IntPoint = new IntPoint(this.mouseX, this.mouseY);
			
			for (idx = 0; idx < actions.length; idx++) {
				var ar:ActionRectangle = actions[idx];
				
				if (ar.ActionType != ActionRectangle.atResizer) continue;
				if (!ar.ActRect.containsPoint(pp)) continue;
				if (ar.Attachment == null) continue;
				
				var column:TreeGridViewColumn = ar.Attachment;
				
				mCursor = Cursor.createCursor(Cursor.H_RESIZE_CURSOR);
				CursorManager.getManager().showCustomCursor(mCursor);
				initMovingSprite(mouseX, mouseY);
				
				currentResizeColumn = column;
				oldColWidth = currentResizeColumn.Size;
				mouseOperation = moColumnResize;
				oldMouseY = this.mouseY;
				oldMouseX = this.mouseX;				
			}
			if (mouseOperation != moNope) return;
			
		}
		
		private function initMovingSprite(atX:int, atY:int):void {
			movingSprite.x = atX;
			movingSprite.y = headerHeight;
			movingSprite.graphics.clear();
			movingSprite.graphics.lineStyle(2, ASColor.BLACK.getRGB(), .5);
			movingSprite.graphics.moveTo(0, 0);
			movingSprite.graphics.lineTo(0, this.height);
			movingSprite.graphics.lineStyle(undefined);
		}
		private function resetMovingSprite():void{
			movingSprite.graphics.clear();
		}
		private function updateMovingSprite(dX:int):void{
			movingSprite.x += dX;
		}
		private function onTVMouseMove(e:MouseEvent):void {
			if (mouseOperation != moColumnResize) return;
			if (currentResizeColumn == null) return;
			
			var dx:int = this.mouseX - oldMouseX;
			currentResizeColumn.Size += dx;
			if (currentResizeColumn.Size < 15) currentResizeColumn.Size = 15;
			else updateMovingSprite(dx);
			oldMouseX = this.mouseX;
		}
		
		
		public function addColumn(value:*,size:int=75, resizeable:Boolean=false, headerBgColor:ASColor = null, 
				headerBorderColor:ASColor = null,
				columnBgColor:ASColor = null, 
				columnBorderColor:ASColor = null ):TreeGridViewColumn{
			
			var ret:TreeGridViewColumn;
			if (value is TreeGridViewColumn) {
				ret = value as TreeGridViewColumn;
				Columns.push(ret);
			}else {
				ret = new TreeGridViewColumn(value, size, resizeable, headerBgColor, headerBorderColor,
					columnBgColor,
					columnBorderColor);
				Columns.push(ret);
			}
			return ret;
		}
		public function get ROOT():TreeGridViewItem { return dataModel.getRoot() as TreeGridViewItem; }
		
		public function get ColCount():int { return Columns.length; }
		
		public function get PaddingLeft():int { return paddingLeft; }
		
		public function set PaddingLeft(value:int):void {
			paddingLeft = value;
		}
		
		public function get HeaderHeight():int { return headerHeight; }
		
		public function set HeaderHeight(value:int):void {
			headerHeight = value;
		}
		
		public function get MinimumColWidth():int { return minimumColWidth; }
		
		public function set MinimumColWidth(value:int):void {
			minimumColWidth = value;
		}
		
		public function get CellSpacing():int { return cellSpacing; }
		
		public function set CellSpacing(value:int):void {
			cellSpacing = value;
		}
		
		public function get CellPadding():int { return cellPadding; }
		
		public function set CellPadding(value:int):void {
			cellPadding = value;
		}
		
		public function get CellHeight():int { return cellHeight; }
		
		public function set CellHeight(value:int):void {
			cellHeight = value;
		}
		public function get TreeColumn():int { return treeColumn; }
		
		public function set TreeColumn(value:int):void 
		{
			treeColumn = value;
		}
		public function get TgvRenderer():TreeGridViewRenderer { return tgvRenderer; }
		
		public function set TgvRenderer(value:TreeGridViewRenderer):void {
			tgvRenderer = value;
		}
		
		public function get Selected():ActionRectangle { return selected; }
		
		public function get SelectedBgColor():ASColor { return selectedBgColor; }
		
		public function set SelectedBgColor(value:ASColor):void {
			selectedBgColor = value;
		}
		
		public function get SelectedBorderColor():ASColor { return selectedBorderColor; }
		
		public function set SelectedBorderColor(value:ASColor):void {
			selectedBorderColor = value;
		}
		
		public function get SelectedCellBgColor():ASColor { return selectedCellBgColor; }
		
		public function set SelectedCellBgColor(value:ASColor):void {
			selectedCellBgColor = value;
		}

		public function get SelectedCell():ActionRectangle { return selectedCell; }
		
		public function getModel():TreeGridViewModel { return dataModel; }
		
		public function setModel(value:TreeGridViewModel):void {
			dataModel = value;
		}
		
		private function renderColumnResizer(dsprite:Sprite, gfx:Graphics,bounds:IntRectangle,column:TreeGridViewColumn):void {
			var icnName:String = "columnResize";
			var icn:AssetIcon = IcnTreeGridView.getEmbededIcon(icnName);
			if (icn == null) return;
			
			var dsp:DisplayObject = icn.getAsset();
			dsp.x = bounds.x+bounds.width-dsp.width;
			dsp.y = bounds.y + (bounds.height - dsp.height) / 2;
			dsprite.addChild(dsp);
			
			
			var actRect:IntRectangle = new IntRectangle(dsp.x, dsp.y, dsp.width, dsp.height);
			actions.push(	new ActionRectangle(	actRect,
													ActionRectangle.atResizer,column));
		}
		private function renderTreeGridViewItem(sprite:Sprite, bounds:IntRectangle, columnIdx:int, item:TreeGridViewItem):void {
			if (tgvRenderer != null) {
				var actRect:IntRectangle = new IntRectangle(bounds.x, bounds.y, bounds.width, bounds.height);
				actions.push(	new ActionRectangle(	actRect,
													ActionRectangle.atCell,item,columnIdx));
				tgvRenderer.renderCellContent(sprite, bounds, columnIdx, item);
			}
		}
		private function renderColumnHeader(sprite:Sprite,gfx:Graphics,bounds:IntRectangle,column:TreeGridViewColumn):void {
			
			if (tgvRenderer != null)
				tgvRenderer.renderColumnHeaderBox(sprite, bounds, column);
			
			
			var widthPatch:int = 0;
			if(column.Resizeable)
				widthPatch = resizerPadding;
				
			var txtRect:IntRectangle = new IntRectangle(
				bounds.x + cellPadding,
				bounds.y + cellPadding,				
				bounds.width - 2 * cellPadding-widthPatch,
				bounds.height - 2 * cellPadding
			);
			
			
			if (tgvRenderer != null)
				tgvRenderer.renderColumnHeaderContent(sprite, txtRect, column);
			
			if (column.Resizeable)
				renderColumnResizer(sprite, gfx, bounds, column);
		}
		private function renderColumnBg(sprite:Sprite, gfx:Graphics, bounds:IntRectangle, column:TreeGridViewColumn):void {
			var nBounds:IntRectangle = bounds.clone();
			nBounds.height = lastHeight - headerHeight;
			nBounds.y += headerHeight;
			
			if (tgvRenderer != null)
				tgvRenderer.renderColumnBg(sprite, nBounds, column);
		}

		private function clearSprite(dest:Sprite):void {
			dest.graphics.clear();
			while(dest.numChildren>0)
				dest.removeChildAt(0);
		}
		private function recalcWidths(b:IntRectangle):void {
			var bounds:IntRectangle = new IntRectangle();
			var lastColumn:Boolean = false;
			var idx:int = 0;
			for (idx = 0; idx < Columns.length; idx++ ) {
				lastColumn = idx + 1 == Columns.length;
				
				var curColumn:TreeGridViewColumn = Columns[idx] as TreeGridViewColumn;
				var curSize:int = curColumn.Size;
				
				if (lastColumn) {
					var nw:int = b.width - bounds.x;
					if (nw > curSize) curSize = nw - cellSpacing;
					
				}
				curColumn.PaintedWidth = curSize;
				
				bounds.width = curSize;
				bounds.move(bounds.width+cellSpacing, 0);
			}
		}
		override protected function paint(b:IntRectangle):void {
			actions = new Array();
			clearSprite(overlay);
			clearSprite(columnBg);
			clearSprite(selectSprite);
			clearSprite(movingSprite);
			
			lastHeight = 0;
			recalcWidths(b);
			drawItems(overlay, b, -1, cellSpacing, headerHeight + cellSpacing, dataModel.getRoot() as TreeGridViewItem);
			
			if (lastHeight < b.height) {
				lastHeight = b.height;
			}
			
			drawColumns(overlay, b);
			
		}
		private function drawFolderIcon(sprite:Sprite, bounds:IntRectangle,item:TreeGridViewItem):void {
			var icnName:String = "openFolder";
			if (item.IsOpen) icnName = "closeFolder";
			
			var icn:AssetIcon = IcnTreeGridView.getEmbededIcon(icnName);
			if (icn == null) return;
			var dsp:DisplayObject = icn.getAsset();
			dsp.x = bounds.x;
			dsp.y = bounds.y + (bounds.height - dsp.height) / 2;
			sprite.addChild(dsp);
			var actRect:IntRectangle = new IntRectangle(dsp.x, dsp.y, dsp.width, dsp.height);
			actions.push(	new ActionRectangle(	actRect,
													ActionRectangle.atFolderIcon,item));
		}
		private function drawItem(targetSprite:Sprite, b:IntRectangle, treeDeep:int, xOff:int, yOff:int,item:TreeGridViewItem):void {
			if (dataModel.getRoot() == item) return;
			
			var cc:int = 0;
			var bounds:IntRectangle = new IntRectangle(0, yOff + lastHeight + cellPadding);
			
			for (cc = 0; cc < Columns.length; cc++) {
				var curColumn:TreeGridViewColumn = Columns[cc] as TreeGridViewColumn;
//calc bounds Rect!!
				var xPatch:int = 0;
				if (cc == treeColumn){
					xPatch = (treeDeep) * (paddingFolderIcon + 2)+ (paddingFolderIcon + 2);
				}
				bounds.move(xOff + xPatch,0);
				bounds.height = cellHeight - 2 * cellPadding;
				bounds.width = curColumn.PaintedWidth - xPatch - 2 * cellPadding;
				renderTreeGridViewItem(targetSprite, bounds, cc, item);
				if (cc == treeColumn && item.IsFolder) {
					var nBounds:IntRectangle = bounds.clone();
					nBounds.move(-(paddingFolderIcon + 2),0);
					drawFolderIcon(targetSprite, nBounds, item);
				}
				bounds.move(bounds.width,0);
			}
			/*select-row*/
			var actRect:IntRectangle = new IntRectangle(0, yOff + lastHeight + cellPadding, bounds.x-cellSpacing, cellHeight);
			actions.push(	new ActionRectangle(	actRect,
													ActionRectangle.atRow,item));
			lastHeight += cellHeight + cellSpacing;
		}
		private function drawItems(targetSprite:Sprite, b:IntRectangle, treeDeep:int, xOff:int,yOff:int,item:TreeGridViewItem):void {
			drawItem(targetSprite, b, treeDeep, xOff, yOff, item);
			
			if (item.ChildCount <= 0) return;
			if (!item.IsOpen) return;
			
			var idx:int = 0;			
			for (idx = 0; idx < item.ChildCount; idx++ ) {
				drawItems(targetSprite, b, treeDeep+1, xOff, yOff, item.Childs[idx] as TreeGridViewItem);
			}
		}
		private function drawColumns(targetSprite:Sprite, b:IntRectangle):void {
			var gfx:Graphics = targetSprite.graphics;
			var sprite:Sprite = targetSprite;
			
			var bounds:IntRectangle = new IntRectangle();
			var lastColumn:Boolean = false;
			
			bounds.height = headerHeight;
			var idx:int = 0;
			for (idx = 0; idx < Columns.length; idx++ ) {
				lastColumn = idx + 1 == Columns.length;
				
				var curColumn:TreeGridViewColumn = Columns[idx] as TreeGridViewColumn;
				
				bounds.width = curColumn.PaintedWidth;
				
				renderColumnHeader(sprite, gfx, bounds, curColumn);
				renderColumnBg(columnBg, columnBg.graphics, bounds, curColumn);
				
					
				bounds.move(bounds.width+cellSpacing, 0);
				
				/*no column-line for last column*/
				if (lastColumn) continue;
				
				gfx.lineStyle(1, ASColor.LIGHT_GRAY.getRGB());
				gfx.moveTo(bounds.x+(cellSpacing/2)-cellSpacing, headerHeight);
				gfx.lineTo(bounds.x+(cellSpacing/2)-cellSpacing, lastHeight);
				
				gfx.lineStyle(undefined);
			}
			lastWidth = bounds.x;
		}
	}
	
}